相關支援其實在目前的LTS版本已經沒問題了(寫這篇文章時,是8.9.4 LTS),所以只要從nodejs.org網站上下載並安裝,就可以跑WebAssembly。我沒有去查Release Note,所以不是很確定從哪個版本開始直接支援,哪個版本要透過--expose-wasm
來打開。
node.js環境所支援的,是跟WebAssembly的Javascript API一致的介面,所以可以直接透過這個介面來使用。但是我希望之前寫的wasm_util.js可以同時支援網頁及node.js環境,所以稍微調整了一下程式:
(function(global) {
let _fetch;
let node_context = false;
if(typeof exports !== 'undefined') {
if(typeof module !== 'undefined' && !!(module.exports)) {
_fetch = function(path) {
let fs = require('fs');
return new Promise((resolve, reject) => {
fs.readFile(path, (err, buf) => {
if(!!err) {
reject(err);
return;
}
resolve({
arrayBuffer: () => {
return new Promise((resolve, reject) => {
resolve(buf);
});
}
});
})
});
}
node_context = true;
} else {
throw new Exception('module.exports not supported?');
}
} else {
_fetch = fetch;
}
(function(fetch) {
if(node_context) {
exports = module.exports = Wasm;
} else {
global.Wasm = Wasm;
}
function Wasm(_url) {
let module = null;
let url = _url;
this.getModule = getModule;
this.getInstance = getInstance;
function getModule() {
if(module === null) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => response.arrayBuffer())
.then(buf => {
WebAssembly.compile(buf)
.then(_module => {
module = _module;
resolve(_module)
});
})
.catch(reason => reject(reason))
});
} else {
return new Promise((resolve, reject) => resolve(module));
}
};
function getInstance(importObjects) {
if(module === null) {
return new Promise((resolve, reject) => {
getModule()
.then(_module => {
if(!!importObjects) resolve(new WebAssembly.Instance(_module, importObjects));
else resolve(new WebAssembly.Instance(_module));
})
.catch(reason => reject(reason))
});
} else {
return new Promise((resolve, reject) => {
if(!!importObjects) resolve(new WebAssembly.Instance(module, importObjects));
else resolve(new WebAssembly.Instance(module));
});
}
}
}
})(_fetch);
})(this);
修改的部份,簡單說就是能不改的地方就不改,然後用module.exports
是否存在來做判斷是否為node.js的module,然後依照原本使用fetch API的方式,做一個fake給他呼叫。
WebAssembly的部份,就拿之前做過的Fibonacci來當例子:
(module
(func $fibn (param $cur i32) (param $nex i32) (param $n i32) (result i32)
get_local $n
i32.const 0
i32.eq ;;param for if
if (result i32)
get_local $cur
else
get_local $nex ;;1st param for $fibn
get_local $cur
get_local $nex
i32.add ;;2nd param for $fibn
get_local $n
i32.const 1
i32.sub ;;3rd param for fibn
call $fibn
end
)
(func (export "fib") (param $n i32) (result i32)
i32.const 0
i32.const 1
get_local $n
call $fibn
)
)
然後寫一個最簡單的node.js程式來跑跑看:
const Wasm = require('../wasm_util');
new Wasm('test019.wasm').getInstance()
.then(instance => {
console.log(instance.exports.fib(10));
})
跑完可以看到console中出現55
,就表示成功了。
明天來把wasm_util.js
放到github,這樣可以用npm透過github網只來引入,使用會比較方便。另外,也試一下更複雜的範例。